Skip to content

Comments

fix: allowed_tools=[] treated as falsy instead of disabling all tools#1

Open
drillan wants to merge 67 commits intomainfrom
fix/allowed-tools-empty-list-523
Open

fix: allowed_tools=[] treated as falsy instead of disabling all tools#1
drillan wants to merge 67 commits intomainfrom
fix/allowed-tools-empty-list-523

Conversation

@drillan
Copy link
Owner

@drillan drillan commented Jan 28, 2026

Summary

  • Changed allowed_tools default from [] to None in ClaudeAgentOptions to distinguish "unset" from "explicitly empty"
  • Updated truthiness check to is not None in subprocess_cli.py so allowed_tools=[] correctly passes --allowedTools "" to the CLI
  • Added tests for both empty-list and default (None) cases

Fixes anthropics#523

Test plan

  • python -m mypy src/ — no errors
  • python -m ruff check src/ tests/ — all checks passed
  • python -m pytest tests/test_transport.py -k allowed_tools — 2 new tests pass
  • python -m pytest tests/test_types.py — 14 tests pass (updated default assertion)
  • python -m pytest tests/ — all 131 tests pass

🤖 Generated with Claude Code

actions-user and others added 30 commits January 21, 2026 22:00
This PR updates the version to 0.1.21 after publishing to PyPI.

## Changes
- Updated version in `pyproject.toml` to 0.1.21
- Updated version in `src/claude_agent_sdk/_version.py` to 0.1.21
- Updated `CHANGELOG.md` with release notes

## Release Information
- Published to PyPI: https://pypi.org/project/claude-agent-sdk/0.1.21/
- Bundled CLI version: 2.1.15
- Install with: `pip install claude-agent-sdk==0.1.21`

🤖 Generated by GitHub Actions

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
…nthropics#488)

## Summary

### Goal: Fully Automated SDK Releases on CLI Bumps

When the bundled CLI version is bumped (e.g., `chore: bump bundled CLI
version to 2.1.12`), we want the SDK to automatically publish a new
patch version to PyPI without any manual intervention. This PR
implements that automation.

### How it works

```
Push: "chore: bump bundled CLI version to X.Y.Z"
         ↓
    Test workflow runs (lint, tests, e2e)
         ↓ (on success)
    auto-release.yml triggers
         ↓
    ┌─ Verify commit message + _cli_version.py changed
    ├─ Calculate next version: 0.1.20 → 0.1.21
    ├─ Build 4 platform wheels (linux, linux-arm, macos, windows)
    ├─ Publish to PyPI
    ├─ Update _version.py, pyproject.toml
    ├─ Generate changelog with Claude
    ├─ Push directly to main (via deploy key)
    └─ Create git tag + GitHub Release
```

### Changes

- **`auto-release.yml`** (new): Triggers on CLI bump commits after Test
workflow passes. Uses deploy key to push directly to main.
- **`build-and-publish.yml`** (new): Reusable workflow for building
wheels and publishing. Supports both direct-push (auto) and PR (manual)
flows.
- **`publish.yml`** (updated): Now calls the reusable workflow. Still
available for manual releases (SDK-only changes, major/minor bumps).
- Both publish flows now use the `production` environment for secrets
(`DEPLOY_KEY`, `PYPI_API_TOKEN`).

### Setup Required

1. Create `production` environment in repo settings
2. Add `DEPLOY_KEY` secret (SSH deploy key with write access)
3. Move `PYPI_API_TOKEN` to production environment

## Test plan
- [ ] Verify workflow syntax is valid in GitHub Actions
- [ ] Test with actual CLI version bump commit
- [ ] Confirm wheels build on all 4 platforms
- [ ] Verify PyPI publishing and GitHub release creation

## Changelog
<!-- CHANGELOG:START -->
N/A - Internal CI/CD workflow changes only
<!-- CHANGELOG:END -->

🤖 Generated with [Claude Code](https://claude.ai/code)
…ics#504)

## Summary
- Adds `contents: write` and `pull-requests: write` permissions to the
`release` job in the auto-release workflow
- This ensures the reusable `build-and-publish` workflow has the
necessary permissions to create releases and interact with pull requests

## Test plan
- [ ] Verify the auto-release workflow runs successfully on the next
version bump commit
- [ ] Confirm the release job can create GitHub releases and update pull
requests as expected

## Changelog
<!-- CHANGELOG:START -->
<!-- CHANGELOG:END -->

🤖 Generated with [Claude Code](https://claude.com/claude-code) (100%
1-shotted by claude)

Co-authored-by: Claude <noreply@anthropic.com>
## Summary
Having tool_use_result on UserMessage allows for rich formatting of
Claude's built-in tools in programs that use the SDK. Would love to gain
access to this.

Co-authored-by: Ron Mordechai <ronmrdechai@fb.com>
…ropics#511)

## Summary
- The auto-release workflow's "Push to main" step could fail when the
remote URL is HTTPS, as it may not have proper credentials configured.
- This change explicitly sets the remote URL to the SSH endpoint
(`git@github.com:anthropics/claude-agent-sdk-python.git`) before
pushing, ensuring authentication works correctly with deploy keys or SSH
credentials.

## Test plan
- [ ] Verify the auto-release workflow triggers and successfully pushes
to main using the SSH remote URL.

## Changelog
<!-- CHANGELOG:START -->
<!-- CHANGELOG:END -->

🤖 Generated with [Claude Code](https://claude.com/claude-code) (100%
9-shotted by claude)

Co-authored-by: Claude <noreply@anthropic.com>
## Summary

- Bumps version from 0.1.21 to 0.1.22 in `pyproject.toml` and
`src/claude_agent_sdk/_version.py`
- Updates `CHANGELOG.md` with new entries for features, bug fixes, and
internal changes included in this release

## Test plan

- [ ] Verify version strings are consistent across `pyproject.toml` and
`_version.py`
- [ ] Verify changelog entry is properly formatted and references
correct PRs
- [ ] Confirm package builds successfully with the new version

## Changelog
<!-- CHANGELOG:START -->
Release v0.1.22 with the following changes:
- Added `tool_use_result` field to `UserMessage` (anthropics#495)
- Added permissions to release job in auto-release workflow (anthropics#504)
- Updated bundled Claude CLI to version 2.1.19
- Extracted build-and-publish workflow into reusable component (anthropics#488)
<!-- CHANGELOG:END -->

🤖 Generated with [Claude Code](https://claude.com/claude-code) (100%
11-shotted by claude)

Co-authored-by: Claude <noreply@anthropic.com>
…cs#516)

## Motivation

The Claude Investigator app, which uses the python claude agent SDK,
needs to query MCP server connection status (example
[here](https://github.com/anthropics/anthropic/blob/948dc8cc4a739b7d026d54e8da2e175cd1659340/ts-threat-intel/ts_threat_intel/claude_investigator/services/client_actor.py#L554-L557)).
Currently it does this by reaching into private SDK internals:

```python
if client._query and hasattr(client._query, "_send_control_request"):
    mcp_status_response = await client._query._send_control_request(
        {"subtype": "mcp_status"}
    )
```

This is fragile — any SDK refactor silently breaks the caller, and the
`hasattr` guard means failures degrade silently to no MCP status
reporting.

## Changes

Add a public `get_mcp_status()` method following the exact same pattern
as `interrupt()`, `set_permission_mode()`, `set_model()`, and
`rewind_files()`:

- **`Query.get_mcp_status()`** — sends `{"subtype": "mcp_status"}`
control request via `_send_control_request`
- **`ClaudeSDKClient.get_mcp_status()`** — public wrapper with
connection check, docstring with Returns and Example sections

Callers can now simply do:

```python
mcp_status_response = await client.get_mcp_status()
```

## Testing

The method delegates entirely to the existing `_send_control_request`
infrastructure which is already tested. The new code is a 3-line wrapper
with no branching logic.
Co-Authored-By: Claude (claude-opus-4-5) <noreply@anthropic.com>
Co-Authored-By: Claude (claude-opus-4-5) <noreply@anthropic.com>
## Summary
- Add `PostToolUseFailureHookInput` type with fields for `tool_name`,
`tool_input`, `tool_use_id`, `error`, and optional `is_interrupt`
- Add `PostToolUseFailureHookSpecificOutput` type with
`additionalContext` field
- Add `"PostToolUseFailure"` to `HookEvent`, `HookInput`, and
`HookSpecificOutput` union types
- Export new types from `__init__.py` and add to `__all__`

## Test plan
- [ ] Verify `PostToolUseFailureHookInput` can be constructed with
required and optional fields
- [ ] Verify `PostToolUseFailureHookSpecificOutput` accepts valid output
shapes
- [ ] Verify type checking passes with `mypy`
- [ ] Verify existing tests still pass

## Changelog
<!-- CHANGELOG:START -->
Added `PostToolUseFailure` hook event type for handling tool use
failures, including `PostToolUseFailureHookInput` and
`PostToolUseFailureHookSpecificOutput` types.
<!-- CHANGELOG:END -->

🤖 Generated with [Claude Code](https://claude.com/claude-code) (0%
4-shotted by claude)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
- Add print statements to MCP e2e tests for debug visibility of message
types and content
- Specify `model="claude-opus-4-5"` in the permission enforcement test
- Print the `executions` dict in the permission test for easier
debugging

## Test plan
- [ ] Run `python -m pytest e2e-tests/test_sdk_mcp_tools.py -v -s` to
verify debug output appears
- [ ] Confirm permission enforcement test passes with specified model

## Changelog
<!-- CHANGELOG:START -->
<!-- CHANGELOG:END -->

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
- Remove the `python-version` matrix from `test`, `test-e2e`, and
`test-examples` CI jobs
- Pin all jobs to Python 3.13 directly instead of testing across 3.10,
3.11, 3.12, and 3.13
- Reduces CI matrix size and speeds up workflow runs

## Test plan
- [ ] Verify CI passes on all three OS targets (ubuntu, macos, windows)
with Python 3.13
- [ ] Confirm unit tests, e2e tests, and example tests all run
successfully

## Changelog
<!-- CHANGELOG:START -->
<!-- CHANGELOG:END -->

🤖 Generated with [Claude Code](https://claude.com/claude-code) (0%
1-shotted by claude)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
- Update MCP permission enforcement e2e test to execute greet and echo
tools sequentially instead of in parallel
- Ensures deterministic tool call ordering for more reliable permission
validation

## Test plan
- [ ] Run `python -m pytest
e2e-tests/test_sdk_mcp_tools.py::test_sdk_mcp_permission_enforcement` to
verify the test passes with sequential execution
- [ ] Confirm greet tool is called before echo tool in the test output

## Changelog
<!-- CHANGELOG:START -->
<!-- CHANGELOG:END -->

🤖 Generated with [Claude Code](https://claude.com/claude-code) (0%
4-shotted by claude)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
- Add `RELEASING.md` documenting the automatic (CLI version bump) and
manual (GitHub Actions UI) release flows
- Simplify `build-and-publish.yml` to always push directly to `main`
(remove PR-based release path)
- Delete `create-release-tag.yml` (was only needed for the PR flow)
- Drop `pull-requests: write` permission from all release workflows
- Fix missing `permissions` block in `publish.yml` that was causing CI
validation failure

## Test plan
- [ ] Verify `publish.yml` passes workflow validation
- [ ] Verify `RELEASING.md` renders correctly on GitHub

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
actions-user and others added 29 commits February 4, 2026 00:42
## Summary
- Add optional `annotations` parameter to the `@tool` decorator and
`SdkMcpTool` dataclass, accepting `ToolAnnotations` from the MCP types
library
- Pass annotations through to MCP `Tool` objects in
`create_sdk_mcp_server` and include them in JSONRPC `tools/list`
responses (only when set, omitted when `None`)
- Re-export `ToolAnnotations` from the top-level package for convenience

## Test plan
- [x] `test_tool_annotations` - Verifies annotations are stored on
`SdkMcpTool` instances and flow through the MCP `list_tools` handler
- [x] `test_tool_annotations_in_jsonrpc` - Verifies annotations are
correctly serialized in JSONRPC `tools/list` responses and omitted when
not set
- [x] All 8 tests in `test_sdk_mcp_integration.py` pass
- [x] `ruff check` and `ruff format` pass
- [x] `mypy` has no new errors (pre-existing issues only)

## Changelog
<!-- CHANGELOG:START -->
- Added support for MCP tool annotations via the `@tool` decorator's new
`annotations` parameter, allowing developers to specify metadata hints
like `readOnlyHint`, `destructiveHint`, `idempotentHint`, and
`openWorldHint`
- Re-exported `ToolAnnotations` from `claude_agent_sdk` for convenience
<!-- CHANGELOG:END -->

🤖 Generated with [Claude Code](https://claude.com/claude-code) (0%
3-shotted by claude)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…t SDK (anthropics#468)

## Summary

This PR fixes the issue where large agent definitions would silently
fail to register. Previously, agents were passed via the `--agents` CLI
flag which has platform-specific size limits (ARG_MAX). When agents
exceeded these limits, a temp file workaround with `@filepath` was used,
but the CLI silently failed to parse it.

## Changes

This aligns the Python SDK with the TypeScript SDK approach:

- **Always use streaming mode internally** (`--input-format
stream-json`) - even for string prompts
- **Send agents via the initialize control request** through stdin (no
size limits)
- **Write string prompts to stdin** after initialize (instead of using
`--print`)
- **Remove the `--agents` CLI flag** and temp file handling entirely

## Why this works

The TypeScript SDK always uses the control protocol with stdin/stdout.
The `initialize` request is sent via stdin which has no ARG_MAX
constraints, allowing arbitrarily large agent definitions.

## Testing

- Added E2E tests with 260KB+ agent payloads (20 agents × 13KB prompts)
- Verified agents are registered correctly in both `ClaudeSDKClient` and
`query()` function
- Tested WITHOUT the fix to confirm the old behavior fails silently
(0/20 agents registered)
- All 132 unit tests pass
- All 8 E2E agent tests pass

## Before/After

| Scenario | Before | After |
|----------|--------|-------|
| 260KB agents via `ClaudeSDKClient` | ❌ 0/20 registered (silent
failure) | ✅ 20/20 registered |
| 260KB agents via `query()` | ❌ 0/20 registered (silent failure) | ✅
20/20 registered |

Closes the issue raised in
anthropics/claude-cli-internal#13749
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Update all GitHub Actions workflows to use `claude-opus-4-6` model
- Enable explicit model selection in `claude-code-review.yml` and
`claude.yml` (previously commented out)
- Update model from `claude-opus-4-5` to `claude-opus-4-6` in
`build-and-publish.yml`

## Test plan
- [ ] Verify `build-and-publish.yml` workflow runs successfully with new
model
- [ ] Verify `claude-code-review.yml` workflow triggers and uses correct
model on PR review
- [ ] Verify `claude.yml` workflow triggers and uses correct model on
issue/PR mention

## Changelog
<!-- CHANGELOG:START -->
<!-- CHANGELOG:END -->

🤖 Generated with [Claude Code](https://claude.com/claude-code) (0%
1-shotted by claude-opus-4-6)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Adds `ThinkingConfig` union type with `ThinkingConfigAdaptive`,
`ThinkingConfigEnabled`, and `ThinkingConfigDisabled` variants
- Adds `thinking` field to `ClaudeAgentOptions` that takes precedence
over the deprecated `max_thinking_tokens` field
- Adds `effort` field to `ClaudeAgentOptions` supporting `"low"`,
`"medium"`, `"high"`, and `"max"` values, mapped to `--effort` CLI flag
- Resolves `thinking` config to `--max-thinking-tokens` CLI flag:
`adaptive` defaults to 32,000, `enabled` uses `budget_tokens`,
`disabled` uses 0

## Test plan
- [ ] Verify `ThinkingConfigEnabled` with `budget_tokens` passes the
correct value to `--max-thinking-tokens`
- [ ] Verify `ThinkingConfigAdaptive` defaults to `--max-thinking-tokens
32000` when `max_thinking_tokens` is not set
- [ ] Verify `ThinkingConfigDisabled` passes `--max-thinking-tokens 0`
- [ ] Verify `thinking` takes precedence over `max_thinking_tokens` when
both are set
- [ ] Verify `effort` is passed as `--effort <value>` to the CLI
- [ ] Verify all new types are exported from `__init__.py`

## Changelog
<!-- CHANGELOG:START -->
- Added `ThinkingConfig`, `ThinkingConfigAdaptive`,
`ThinkingConfigEnabled`, and `ThinkingConfigDisabled` types to
`ClaudeAgentOptions` for controlling extended thinking behavior
- Added `effort` option to `ClaudeAgentOptions` for controlling thinking
depth (`"low"`, `"medium"`, `"high"`, `"max"`)
- Deprecated `max_thinking_tokens` in favor of the new `thinking` config
field
<!-- CHANGELOG:END -->

🤖 Generated with [Claude Code](https://claude.com/claude-code) (0%
11-shotted by claude)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ll tools

`allowed_tools=[]` was treated as falsy and ignored, causing all tools
to remain available instead of none. Change the default from `[]` to
`None` and use `is not None` check to correctly pass `--allowedTools ""`
to the CLI when an empty list is explicitly provided.

Fixes anthropics#523

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@drillan drillan force-pushed the fix/allowed-tools-empty-list-523 branch from 1489baa to 97caff9 Compare February 15, 2026 02:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

allowed_tools=[] (empty list) is treated as falsy, conflating "no auto-allowed tools" with "unset"

7 participants